package com.wyw.hemerocallis;

import com.wyw.hemerocallis.annotation.IgnorePermission;
import com.wyw.hemerocallis.annotation.WywPermission;
import com.wyw.hemerocallis.permission.HttpMethodEnum;
import com.wyw.hemerocallis.vo.PermissionInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.web.mappings.HandlerMethodDescription;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 接口权限扫描器
 *
 * @author Mr Wu    yewen.wu.china@gmail.com
 * <p>
 * Update History:
 * Author        Time            Content
 */
@Slf4j
public class AnnotationScanner {

    //路径前缀
    private String pathPrefix;

    //扫描的包路径
    private static final String WYW_HEMEROCALLIS_PKG = "com.wyw.hemerocallis";

    AnnotationScanner(String prefix) {
        this.pathPrefix = trimPath(prefix);
    }

    /**
     * 构造所有 Controller 的权限信息
     * */
    List<PermissionInfo> scanPermission(
            Map<RequestMappingInfo, HandlerMethod> mappingMap
    ) {

        List<PermissionInfo> result = new ArrayList<>();
        mappingMap.forEach((mapInfo, method) ->
                result.addAll(buildPermission(mapInfo, method)));

        return result;
    }

    /**
     * 构造Controller的权限信息
     * @param mapInfo       {@link RequestMappingInfo} @RequestMapping对应的信息
     * @param handlerMethod {@link HandlerMethod} @RequestMapping 对应方法的详情，包括方法、类、参数
     * @return
     */
    private List<PermissionInfo> buildPermission(
            RequestMappingInfo mapInfo, HandlerMethod handlerMethod
    ) {
        Method javaMethod = handlerMethod.getMethod();
        Class baseClass = javaMethod.getDeclaringClass();

        //忽略非com.wyw.hemerocallis下的mapping
        if(!isWywHemerocallisPackage(baseClass.getName())) {
            log.debug("ignore method: {}", javaMethod.getName());
            return Collections.emptyList();
        }

        //判断是否需要忽略此方法
        IgnorePermission ignorePermission = javaMethod.getAnnotation(IgnorePermission.class);
        if(null != ignorePermission) {
            log.debug("ignore method:{}", javaMethod.getName());
            return Collections.emptyList();
        }

        //取出权限注解
        WywPermission wywPermission = javaMethod.getAnnotation(
                WywPermission.class
        );
        if(null == wywPermission) {
            //如果没有WywPermission 且没有 IgnorePermission， 在日志中记录
            log.error("lack @WywPermission -> {}#{}",
                    javaMethod.getDeclaringClass().getName(),
                    javaMethod.getName());
            return Collections.emptyList();
        }

        //取出URL
        Set<String> urlSet = mapInfo.getPatternsCondition().getPatterns();

        //取出method
        boolean isAllMethods = false;
        Set<RequestMethod> methodSet = mapInfo.getMethodsCondition().getMethods();
        if (CollectionUtils.isEmpty(methodSet)) {
            isAllMethods = true;
        }

        List<PermissionInfo> infoList = new ArrayList<>();

        for (String url : urlSet) {

            // 支持的 http method 为全量
            if (isAllMethods) {
                PermissionInfo info = buildPermissionInfo(
                        HttpMethodEnum.ALL.name(),
                        javaMethod.getName(),
                        this.pathPrefix + url,
                        wywPermission.readOnly(),
                        wywPermission.description(),
                        wywPermission.extra()
                );
                infoList.add(info);
                continue;
            }

            // 支持部分 http method
            for (RequestMethod method : methodSet) {
                PermissionInfo info = buildPermissionInfo(
                        method.name(),
                        javaMethod.getName(),
                        this.pathPrefix + url,
                        wywPermission.readOnly(),
                        wywPermission.description(),
                        wywPermission.extra()
                );
                infoList.add(info);
                log.info("permission detected: {}", info);
            }
        }

        return infoList;



    }

    /**
     * 构建单个接口的权限信息
     */
    private PermissionInfo buildPermissionInfo(
            String reqMethod, String javaMethod, String path,
            boolean readOnly, String desp, String extra
    ) {
        PermissionInfo info = new PermissionInfo();
        info.setMethod(reqMethod);
        info.setUrl(path);
        info.setIsRead(readOnly);
        info.setDescription(
                // 如果注解中没有描述, 则使用方法名称
                StringUtils.isEmpty(desp) ? javaMethod : desp
        );
        info.setExtra(extra);

        return info;
    }

    /**
     * 判断当前类是否在我们定义的包中
     */
    private boolean isWywHemerocallisPackage(String className) {
        return className.startsWith(WYW_HEMEROCALLIS_PKG);
    }

    /**
     * 保证 path 以/开头，且不以/结尾
     * 如果是user -> /user, /user/ -> /user
     * @param path
     * @return
     */
    private String trimPath(String path) {
        if(StringUtils.isEmpty(path)) {
            return "";
        }

        if(!path.startsWith("/")) {
            path = "/".concat(path);
        }

        if (path.endsWith("/")) {
            path = path.substring(0, path.length() - 1);
        }

        return path;
    }

}
